#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <unistd.h>
/**
 * 链表节点
 */
typedef struct _node_t {
    int data;
    struct _node_t * next;
} node_t;

node_t * head = NULL;

// 互斥锁
pthread_mutex_t mutex;
// 条件变量
pthread_cond_t cond;
/**
 * 改变条件的 线程
 * @param arg
 * @return
 */
void * producer(void * arg){
    //
    while(1){
        //加锁
        pthread_mutex_lock(&mutex);
        //分配节点空间
        node_t * nn = malloc(sizeof(node_t));
        if(nn == NULL){
            break;
        }
        memset(nn,0, sizeof(node_t));
        nn->data = random() % 100 + 1;
        nn->next = NULL;
        nn->next = head;
        //头部插入法
        nn->next = head;
        head = nn;
        printf("producer : %d\n",nn->data);
        //解锁
        pthread_mutex_unlock(&mutex);
        //唤醒 因为条件而阻塞的线程
        pthread_cond_signal(&cond);

        sleep(random() % 3 +1);
    }
    pthread_exit(NULL);
}

/**
 * 等待条件的 线程
 * @param arg
 * @return
 */
void * customer(void * arg){
    node_t * temp = NULL;
    while(1){
        //加锁
        pthread_mutex_lock(&mutex);

        //表示条件不满足
        if(head == NULL){
            //等待条件满足 会阻塞 代码操作中可能会有一次释已存在的锁，让另外的线程获取锁，等收到唤醒信号时，再申请一次锁
            pthread_cond_wait(&cond,&mutex);
        }
        temp = head;
        head = head->next;
        printf("customer : %d\n",temp->data);
        free(temp);

        //解锁
        pthread_mutex_unlock(&mutex);

    }
    return NULL;
}

/**
 * 条件变量 的 生产消费模型
 * @return
 */
int main(){
    pthread_t tid1;
    pthread_t tid2;
    int ret = -1;
    //设置随机种子
    srandom(getpid());
    // 初始化条件变量
    ret = pthread_cond_init(&cond,NULL);
    // 初始化互斥锁
    ret = pthread_mutex_init(&mutex,NULL);
    // 创建线程
    pthread_create(&tid1,NULL,producer,NULL);
    pthread_create(&tid2,NULL,customer,NULL);

    // 回收线程资源
    ret = pthread_join(tid1,NULL);
    ret = pthread_join(tid2,NULL);

    // 销毁互斥锁
    pthread_mutex_destroy(&mutex);
    // 销毁条件变量
    pthread_cond_destroy(&cond);

    printf("main exit...\n");
    return 0;
}
